/**************************************************************
 * encoding_convert.c - 
 * Copyright (c) 2009  Qin Xiaoxue  <crs.chin@gmail.com>
 *
 * This file is  free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
**************************************************************/


#include <stdio.h>
#include <unistd.h>
#include <glib.h>
#include <string.h>
#include <enca.h>

#include "encoding_convert.h"
/*
 *convert file @fp from encoding @from to @to, return the converted
 *file pointer when encountered invalid char seuence if @keepon if
 *TRUE, else return NULL
 **/
FILE    *encoding_convert_to(FILE *fp_from, FILE *fp_to, const gchar *from, const gchar *to, gboolean keepon)
{
  gchar in[2048];
  gchar *out;
  gsize size, read = 0, left = 0;
  gsize written, i, j;
  GIConv iconv;

  if(! fp_from || ! fp_to || ! from || ! to)
    return FALSE;

  rewind(fp_from);
  if(ftruncate(fileno(fp_to), 0) == -1)
	return NULL;

  iconv = g_iconv_open(to, from);
  if(iconv == (GIConv)-1)
    return NULL;


  do{
    if(left)
      memmove(in, in + read, left);

    size = fread(in + left, 1, 2048 - left, fp_from);
    size += left;
    if(size)  {
      out = g_convert_with_iconv(in, size, iconv, &read, &written, NULL);
      if(! out)  {
		if(keepon)
		  break;
		g_iconv_close(iconv);
		return NULL;
      }

      i = 0;
      while(written)  {
		j = fwrite(out + i, 1, written, fp_to);
		written -= j;
		i += j;
      }

      left = size - read;
      g_free(out);
    }
  }while(! feof(fp_from));

  rewind(fp_to);
  g_iconv_close(iconv);

  return fp_to;
}


/*
 *convert file @fp from encoding @from to @to, return the converted
 *file pointer when encountered invalid char seuence if @keepon if
 *TRUE, else return NULL
 **/

/* depreciated */

/* FILE    *encoding_convert_const(FILE *fp, const gchar *from, const gchar *to, gboolean keepon) */
/* { */
/*   gchar in[2048]; */
/*   gchar *out; */
/*   gsize size, read = 0, left = 0; */
/*   gsize written, i, j; */
/*   FILE *res; */
/*   GIConv iconv; */

/*   if(! fp || ! from || ! to) */
/*     return FALSE; */


/*   iconv = g_iconv_open(to, from); */
/*   if(iconv == (GIConv)-1) */
/*     return NULL; */

/*   res = tmpfile(); */
/*   if(! res) */
/*     return NULL; */

/*   rewind(fp); */

/*   do{ */
/*     if(left) */
/*       memmove(in, in + read, left); */

/*     size = fread(in + left, 1, 2048 - left, fp); */
/*     size += left; */
/*     if(size)  { */
/*       out = g_convert_with_iconv(in, size, iconv, &read, &written, NULL); */
/*       if(! out)  { */
/* 	if(keepon) */
/* 	  break; */
/* 	fclose(res); */
/* 	g_iconv_close(iconv); */
/* 	return NULL; */
/*       } */

/*       i = 0; */
/*       while(written)  { */
/* 	j = fwrite(out + i, 1, written, res); */
/* 	written -= j; */
/* 	i += j; */
/*       } */

/*       left = size - read; */
/*       g_free(out); */
/*     } */
/*   }while(! feof(fp)); */

/*   rewind(res); */
/*   g_iconv_close(iconv); */

/*   return res; */
/* } */




/*
 *convert file @fp from encoding @from to @to, return ok when
 *encountered invalid char seuence if @keepon if true, else return
 *FALSE
 **/
gboolean  encoding_convert(FILE *fp, const gchar *from, const gchar *to, gboolean keepon)
{
  FILE *tmp;
  int ret;

  tmp = encoding_convert_const(fp, from, to, keepon);
  if(! tmp)
    return FALSE;
  
  ret = file_copy(tmp, fp);
  fclose(tmp);

  return ret;
}


const gchar  *guess_encoding(const gchar *buf, size_t size, const gchar *lang, int namestyle)
{
  EncaAnalyser al;
  EncaEncoding encoding;
  gssize _size;

  al = enca_analyser_alloc(lang);
  if(! al)
    return NULL;

  if(size == -1)
	_size = strlen(buf);
  else
	_size = size;

  encoding = enca_analyse_const(al, buf, _size);

  enca_analyser_free(al);

  if(encoding.charset == ENCA_CS_UNKNOWN)
    return NULL;

  return enca_charset_name(encoding.charset, namestyle);
}

/*
 * guess the encoding from buf using libenca, and convert it into
 * encoding @to and return it, @out is the returned string length
 */
gchar   *zh_convert(const gchar *buf, size_t size, size_t *out, const gchar *to)
{
  const gchar *from;
  gchar *res;
  GIConv iconv;
  size_t written;

  if(! buf)
    return NULL;

  from = guess_encoding(buf, size, "zh", ENCA_NAME_STYLE_ICONV);

  if(! from)
    return NULL;

#ifndef NDEBUG
  printf("-- guessed encoding from str \"%s\" is %s\n", buf, from);
#endif

  if(! strcmp(from, to))
    return g_strndup(buf, size);

  iconv = g_iconv_open(to, from);
  if(iconv == (GIConv)-1)
    return NULL;

  res = g_convert_with_iconv(buf, size, iconv, NULL, &written, NULL);

  g_iconv_close(iconv);

  if(! res)  {
    if(out)
      *out = 0;
    return NULL;
  }

  if(out)
    *out = written;

  return res;
}

/******************************************************************/
/********************** TESTING CODE *****************************/
/******************************************************************/
#if defined(TEST_LOCALE_CONVERT)

int main(int argc, char **argv)
{
  FILE *fp;
  gchar buf[1024];
  gint size;

  if(argc < 4)  {
    printf("%s FILE from to",argv[0]);
    return 1;
  }

  fp = fopen(argv[1], "r+");

  size = fread(buf, 1, 1024, fp);

  printf("CONVERTED STRING:%s\n", zh_convert(buf, size, NULL, "utf8"));

  if(! encoding_convert(fp, argv[2], argv[3], TRUE))
    printf("** error occurred while converting file .\n");
  else
    printf("-- ok converting .\n");

  fclose(fp);

  return 0;
}



#endif
